package com.ssy.lingxi.logistics.serviceimpl;

import cn.hutool.core.util.NumberUtil;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.logistics.api.dto.request.*;
import com.ssy.lingxi.logistics.api.dto.response.FreightResponse;
import com.ssy.lingxi.logistics.api.dto.response.OrderSeparateFreightListResponse;
import com.ssy.lingxi.logistics.entity.FreightTemplate;
import com.ssy.lingxi.logistics.entity.FreightTemplateDesignate;
import com.ssy.lingxi.logistics.entity.ReceiverAddress;
import com.ssy.lingxi.logistics.model.enums.CommonStatusEnum;
import com.ssy.lingxi.logistics.model.enums.ReceiverAddressCreateTypeEnum;
import com.ssy.lingxi.logistics.model.request.CommonStatusRequest;
import com.ssy.lingxi.logistics.model.request.FreightTemplateListRequest;
import com.ssy.lingxi.logistics.model.request.FreightTemplateRequest;
import com.ssy.lingxi.logistics.model.request.OrderProductListRequest;
import com.ssy.lingxi.logistics.model.response.FreightTemplateListResponse;
import com.ssy.lingxi.logistics.model.response.FreightTemplateResponse;
import com.ssy.lingxi.logistics.repository.FreightTemplateDesignateRepository;
import com.ssy.lingxi.logistics.repository.FreightTemplateRepository;
import com.ssy.lingxi.logistics.repository.ReceiverAddressRepository;
import com.ssy.lingxi.logistics.service.IFreightTemplateService;
import com.ssy.lingxi.product.api.feign.ICommodityFeign;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 运费模板服务实现类
 * @author yzc
 * @version 2.0.0
 * @date 2020/7/14
 */
@Service
public class FreightTemplateServiceImpl implements IFreightTemplateService {

    @Resource
    private FreightTemplateRepository freightTemplateRepository;
    @Resource
    private FreightTemplateDesignateRepository freightTemplateDesignateRepository;
    @Resource
    private ICommodityFeign commodityFeign;
    @Resource
    private ReceiverAddressRepository receiverAddressRepository;
    @Resource
    private Validator validator;

    @Override
    public Wrapper<PageData<FreightTemplateListResponse>> pageFreightTemplate(FreightTemplateListRequest request, UserLoginCacheDTO loginUser) {
        Pageable page = PageRequest.of(request.getCurrent() - 1, request.getPageSize(), Sort.by("id").ascending());

        Page<FreightTemplate> pageList = freightTemplateRepository.findAll((Specification<FreightTemplate>) (root, query, cb) -> {
            List<Predicate> predicateList = new ArrayList<>();
            predicateList.add(cb.equal(root.get("memberId"), loginUser.getMemberId()));
            predicateList.add(cb.equal(root.get("roleId"), loginUser.getMemberRoleId()));
            if (Objects.nonNull(request.getName())) {
                predicateList.add(cb.like(root.get("name"), "%" + request.getName() + "%"));
            }

            Predicate[] p = new Predicate[predicateList.size()];
            return query.where(predicateList.toArray(p)).getRestriction();
        }, page);

        List<FreightTemplateListResponse> resultList = pageList.stream().map(e -> {
            FreightTemplateListResponse freightTemplateListResponse = new FreightTemplateListResponse();
            freightTemplateListResponse.setId(e.getId());
            freightTemplateListResponse.setName(e.getName());
            freightTemplateListResponse.setPricingMode(e.getPricingMode());
            freightTemplateListResponse.setTransportMode(e.getTransportMode());
            freightTemplateListResponse.setWeight(e.getWeight());
            freightTemplateListResponse.setExplain(e.getExplain());
            freightTemplateListResponse.setStatus(e.getStatus());
            return freightTemplateListResponse;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(pageList.getTotalElements(), resultList));
    }

    @Override
    public Wrapper<FreightTemplateResponse> getFreightTemplate(Long id) {
        // 无参返回空
        if (Objects.isNull(id)) {
            return Wrapper.success();
        }
        FreightTemplate freightTemplate = freightTemplateRepository.findById(id).orElse(null);
        if (Objects.isNull(freightTemplate)) {
            return Wrapper.fail(ResponseCode.LS_RECORDS_DON_T_EXIST);
        }

        FreightTemplateResponse freightTemplateResponse = new FreightTemplateResponse();
        freightTemplateResponse.setId(freightTemplate.getId());
        freightTemplateResponse.setName(freightTemplate.getName());
        freightTemplateResponse.setPricingMode(freightTemplate.getPricingMode());
        freightTemplateResponse.setTransportMode(freightTemplate.getTransportMode());
        freightTemplateResponse.setWeight(freightTemplate.getWeight());
        freightTemplateResponse.setPrice(freightTemplate.getPrice());
        freightTemplateResponse.setIncrementWeight(freightTemplate.getIncrementWeight());
        freightTemplateResponse.setIncrementPrice(freightTemplate.getIncrementPrice());
        freightTemplateResponse.setExplain(freightTemplate.getExplain());

        // 获取指定地区运费
        List<FreightTemplateDesignate> templateDesignateList = freightTemplateDesignateRepository.findAllByFreightTemplateId(freightTemplate.getId(), Sort.by("id").ascending());
        if (!CollectionUtils.isEmpty(templateDesignateList)) {
            List<FreightTemplateResponse.FreightTemplateDesignateResponse> designateResponseList = templateDesignateList.stream().map(e -> {
                FreightTemplateResponse.FreightTemplateDesignateResponse designateResponse = new FreightTemplateResponse.FreightTemplateDesignateResponse();
                designateResponse.setId(e.getId());
                designateResponse.setFreightTemplateId(e.getFreightTemplateId());
                designateResponse.setAreaIds(e.getAreaIds());
                designateResponse.setWeight(e.getWeight());
                designateResponse.setPrice(e.getPrice());
                designateResponse.setIncrementWeight(e.getIncrementWeight());
                designateResponse.setIncrementPrice(e.getIncrementPrice());
                return designateResponse;
            }).collect(Collectors.toList());

            freightTemplateResponse.setDesignateList(designateResponseList);
        }

        return Wrapper.success(freightTemplateResponse);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Wrapper<Void> addFreightTemplate(FreightTemplateRequest request, UserLoginCacheDTO loginUser) {
        Wrapper<Void> checkWrapper = checkFreightTemplateParam(request);
        if (ResponseCode.SUCCESS.getCode() != checkWrapper.getCode()) {
            return Wrapper.fail(checkWrapper.getCode(), checkWrapper.getMessage());
        }

        // 保存默认运费模板
        FreightTemplate freightTemplate = new FreightTemplate();
        freightTemplate.setName(request.getName());
        freightTemplate.setPricingMode(request.getPricingMode());
        freightTemplate.setTransportMode(request.getTransportMode());
        freightTemplate.setWeight(request.getWeight());
        freightTemplate.setPrice(request.getPrice());
        freightTemplate.setIncrementWeight(request.getIncrementWeight());
        freightTemplate.setIncrementPrice(request.getIncrementPrice());
        freightTemplate.setExplain(request.getExplain());
        freightTemplate.setStatus(CommonStatusEnum.ENABLE.getCode());
        freightTemplate.setMemberId(loginUser.getMemberId());
        freightTemplate.setRoleId(loginUser.getMemberRoleId());
        freightTemplate.setCreateTime(System.currentTimeMillis());

        freightTemplateRepository.saveAndFlush(freightTemplate);

        // 保存指定地区运费
        saveAllFreightTemplateDesignate(request.getDesignateList(), freightTemplate.getId());

        return Wrapper.success();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Wrapper<Void> updateFreightTemplate(FreightTemplateRequest request, UserLoginCacheDTO loginUser) {
        Wrapper<Void> checkWrapper = checkFreightTemplateParam(request);
        if (ResponseCode.SUCCESS.getCode() != checkWrapper.getCode()) {
            return Wrapper.fail(checkWrapper.getCode(), checkWrapper.getMessage());
        }

        FreightTemplate freightTemplate = freightTemplateRepository.findByIdAndMemberIdAndRoleId(request.getId(), loginUser.getMemberId(), loginUser.getMemberRoleId());
        if (Objects.isNull(freightTemplate)) {
            return Wrapper.fail(ResponseCode.LS_RECORDS_DON_T_EXIST);
        }

        if (CommonStatusEnum.ENABLE.getCode().equals(freightTemplate.getStatus())) {
            return Wrapper.fail(ResponseCode.LS_DATA_STATUS_INCORRECT_OPERATE_INVALID);
        }

        // 更新默认运费模板
        freightTemplate.setName(request.getName());
        freightTemplate.setPricingMode(request.getPricingMode());
        freightTemplate.setTransportMode(request.getTransportMode());
        freightTemplate.setWeight(request.getWeight());
        freightTemplate.setPrice(request.getPrice());
        freightTemplate.setIncrementWeight(request.getIncrementWeight());
        freightTemplate.setIncrementPrice(request.getIncrementPrice());
        freightTemplate.setExplain(request.getExplain());
        freightTemplateRepository.saveAndFlush(freightTemplate);

        // 删除关联指定地区模板
        deleteDesignateByFreightTemplateId(request.getId());

        // 保存指定地区运费
        saveAllFreightTemplateDesignate(request.getDesignateList(), freightTemplate.getId());

        return Wrapper.success();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Wrapper<Void> updateStatusFreightTemplate(CommonStatusRequest request, UserLoginCacheDTO loginUser) {
        FreightTemplate freightTemplate = freightTemplateRepository.findByIdAndMemberIdAndRoleId(request.getId(), loginUser.getMemberId(), loginUser.getMemberRoleId());
        if (Objects.isNull(freightTemplate)) {
            return Wrapper.fail(ResponseCode.LS_RECORDS_DON_T_EXIST);
        }

        freightTemplate.setStatus(request.getStatus());
        freightTemplateRepository.saveAndFlush(freightTemplate);

        return Wrapper.success();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Wrapper<Void> deleteFreightTemplate(CommonIdRequest request, UserLoginCacheDTO loginUser) {
        FreightTemplate freightTemplate = freightTemplateRepository.findByIdAndMemberIdAndRoleId(request.getId(), loginUser.getMemberId(), loginUser.getMemberRoleId());
        if (Objects.isNull(freightTemplate)) {
            return Wrapper.fail(ResponseCode.LS_RECORDS_DON_T_EXIST);
        }

        if (CommonStatusEnum.ENABLE.getCode().equals(freightTemplate.getStatus())) {
            return Wrapper.fail(ResponseCode.LS_DATA_STATUS_INCORRECT_OPERATE_INVALID);
        }

        // 商品关联,不能删除
        Wrapper<Boolean> commodityUseTemplate = commodityFeign.getCommodityUseTemplate(request.getId());
        if (ResponseCode.SUCCESS.getCode() != commodityUseTemplate.getCode()) {
            return Wrapper.fail(ResponseCode.SERVICE_PRODUCT_ERROR);
        }
        if (commodityUseTemplate.getData()) {
            return Wrapper.fail(ResponseCode.LS_DATA_RELATION_PRODUCT_DEL);
        }


        // 删除关联指定地区模板
        deleteDesignateByFreightTemplateId(request.getId());

        // 删除默认运费模板
        freightTemplateRepository.deleteById(freightTemplate.getId());

        return Wrapper.success();
    }

    @Override
    public Wrapper<BigDecimal> calFreightPrice(OrderProductListRequest request) {
        // 订单商品集合
        List<ProductFreightDetail> orderProductList = request.getOrderProductList();

        // 收货地址
        ReceiverAddress receiverAddress = receiverAddressRepository.findById(request.getReceiverAddressId()).orElse(null);
        if (Objects.isNull(receiverAddress)) {
            return Wrapper.fail(ResponseCode.LS_RECORDS_DON_T_EXIST);
        }

        BigDecimal totalSumPrice = this.baseCalFreightPrice(orderProductList, receiverAddress);

        return Wrapper.success(totalSumPrice);
    }

    @Override
    public Wrapper<BigDecimal> calFreightPrice(OrderFreightRequest request) {
        // 订单商品集合
        List<ProductFreightDetail> orderProductList = request.getProductFreightDetailList();

        // 收货地址
        ReceiverAddress receiverAddress = receiverAddressRepository.findById(request.getReceiverAddressId()).orElse(null);
        if (Objects.isNull(receiverAddress)) {
            return Wrapper.fail(ResponseCode.LS_RECORDS_DON_T_EXIST);
        }

        BigDecimal totalSumPrice = this.baseCalFreightPrice(orderProductList, receiverAddress);

        return Wrapper.success(totalSumPrice);
    }

    @Override
    public Wrapper<List<FreightResponse>> batchCalFreightPrice(List<TransferOrderFreightRequest> requestList) {
        List<FreightResponse> responseList = new ArrayList<>();
        for (TransferOrderFreightRequest request : requestList) {
            // 订单商品集合
            List<ProductFreightDetail> orderProductList = request.getProductFreightDetailList();

            // 收货地址
            ReceiverAddress receiverAddress = receiverAddressRepository.findById(request.getReceiverAddressId()).orElse(null);
            if (Objects.isNull(receiverAddress)) {
                return Wrapper.fail(ResponseCode.LS_RECORDS_DON_T_EXIST);
            }

            // 计算运费
            BigDecimal totalSumPrice = this.baseCalFreightPrice(orderProductList, receiverAddress);

            // 转单功能复制一个地址
            ReceiverAddress saveNewReceiverAddress = new ReceiverAddress();
            saveNewReceiverAddress.setReceiverName(receiverAddress.getReceiverName());
            saveNewReceiverAddress.setProvinceCode(receiverAddress.getProvinceCode());
            saveNewReceiverAddress.setProvinceName(receiverAddress.getProvinceName());
            saveNewReceiverAddress.setCityCode(receiverAddress.getCityCode());
            saveNewReceiverAddress.setCityName(receiverAddress.getCityName());
            saveNewReceiverAddress.setDistrictCode(receiverAddress.getDistrictCode());
            saveNewReceiverAddress.setDistrictName(receiverAddress.getDistrictName());
            saveNewReceiverAddress.setAddress(receiverAddress.getAddress());
            saveNewReceiverAddress.setAreaCode(receiverAddress.getAreaCode());
            saveNewReceiverAddress.setPostalCode(receiverAddress.getPostalCode());
            saveNewReceiverAddress.setPhone(receiverAddress.getPhone());
            saveNewReceiverAddress.setTel(receiverAddress.getTel());
            saveNewReceiverAddress.setIsDefault(receiverAddress.getIsDefault());
            // 提交的会员
            saveNewReceiverAddress.setMemberId(request.getMemberId());
            saveNewReceiverAddress.setRoleId(request.getRoleId());
            saveNewReceiverAddress.setCreateTime(System.currentTimeMillis());
            saveNewReceiverAddress.setCreateType(ReceiverAddressCreateTypeEnum.TRANSFER_ORDER.getCode());
            receiverAddressRepository.saveAndFlush(saveNewReceiverAddress);

            FreightResponse freightResponse = new FreightResponse();
            freightResponse.setOrderId(request.getOrderId());
            freightResponse.setMemberId(request.getMemberId());
            freightResponse.setRoleId(request.getRoleId());
            freightResponse.setFreightPrice(totalSumPrice);
            freightResponse.setReceiverAddressId(saveNewReceiverAddress.getId());
            freightResponse.setReceiverName(saveNewReceiverAddress.getReceiverName());
            freightResponse.setProvinceCode(saveNewReceiverAddress.getProvinceCode());
            freightResponse.setProvinceName(saveNewReceiverAddress.getProvinceName());
            freightResponse.setCityCode(saveNewReceiverAddress.getCityCode());
            freightResponse.setCityName(saveNewReceiverAddress.getCityName());
            freightResponse.setDistrictCode(saveNewReceiverAddress.getDistrictCode());
            freightResponse.setDistrictName(saveNewReceiverAddress.getDistrictName());
            freightResponse.setAddress(saveNewReceiverAddress.getAddress());
            freightResponse.setPostalCode(saveNewReceiverAddress.getPostalCode());
            freightResponse.setAreaCode(saveNewReceiverAddress.getAreaCode());
            freightResponse.setPhone(saveNewReceiverAddress.getPhone());
            freightResponse.setTel(saveNewReceiverAddress.getTel());
            freightResponse.setIsDefault(saveNewReceiverAddress.getIsDefault());

            responseList.add(freightResponse);
        }

        return Wrapper.success(responseList);
    }

    @Override
    public Wrapper<Map<OrderSeparateFreightRequest, BigDecimal>> findSeparateOrderFreight(Map<OrderSeparateFreightRequest, List<OrderSeparateProductFreightDetail>> products) {
        //Step 1: 先判断收货地址Id是否全部大于0， 如果没有大于0的，运费为0
        List<Long> receiverAddressIds = products.values().stream().flatMap(Collection::stream).map(OrderSeparateProductFreightDetail::getReceiverAddressId).filter(id -> id.compareTo(0L) > 0).distinct().collect(Collectors.toList());
        if(CollectionUtils.isEmpty(receiverAddressIds)) {
            return Wrapper.success(products.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, p -> BigDecimal.ZERO)));
        }

        //Step 2: 批量查询收货地址
        List<ReceiverAddress> receiverAddresses = receiverAddressRepository.findAllById(receiverAddressIds);

        //Step 3: 直接映射返回结果
        Map<OrderSeparateFreightRequest, BigDecimal> resultMap = products.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, product -> {
            //如果没有商品列表，运费为0
            if(CollectionUtils.isEmpty(product.getValue())) {
                return BigDecimal.ZERO;
            }

            //按照收货地址分组
            Map<Long, List<OrderSeparateProductFreightDetail>> orderProductMap = product.getValue().stream().collect(Collectors.groupingBy(OrderSeparateProductFreightDetail::getReceiverAddressId));

            //如果分组后只有一条数据，且数据的收货地址Id为0，运费为0
            if(CollectionUtils.isEmpty(orderProductMap) || (orderProductMap.size() == 1 && orderProductMap.containsKey(0L))) {
                return BigDecimal.ZERO;
            }

            //用reduce()方法，累计求运费和
            return orderProductMap.entrySet().stream().map(entry -> {
                //如果收货地址Id为0，运费为0
                if(entry.getKey().equals(0L)) {
                    return BigDecimal.ZERO;
                }

                //根据收货地址Id，找到收货地址，如果收货地址为Null，运费为0
                ReceiverAddress receiverAddress = receiverAddresses.stream().filter(address -> address.getId().equals(entry.getKey())).findFirst().orElse(null);
                if(Objects.isNull(receiverAddress)) {
                    return BigDecimal.ZERO;
                }

                //在分组的商品列表中过滤出运费模板id大于0，且数量大于0、重量大于0的商品列表，如果为空，运费为空，否则批量计算
                List<ProductFreightDetail> productFreightDetailList = entry.getValue().stream().filter(p -> p.getTemplateId().compareTo(0L) > 0 && p.getCount().compareTo(0D) > 0 && p.getWeight().compareTo(0D) > 0).map(e -> {
                    ProductFreightDetail productFreightDetail = new ProductFreightDetail();
                    productFreightDetail.setTemplateId(e.getTemplateId());
                    productFreightDetail.setCount(e.getCount());
                    productFreightDetail.setWeight(e.getWeight());
                    return productFreightDetail;
                }).collect(Collectors.toList());
                return CollectionUtils.isEmpty(productFreightDetailList) ? BigDecimal.ZERO : this.baseCalFreightPrice(productFreightDetailList, receiverAddress);
            }).reduce(BigDecimal.ZERO, BigDecimal::add);
        }));

        return Wrapper.success(resultMap);
    }

    /**
     * 内部接口 - 批量查询运费（拆单）
     *
     * @param requests 拆单后的供应商及商品信息列表
     * @return 查询结果
     */
    @Override
    public Wrapper<List<OrderSeparateFreightListResponse>> findSeparateOrderFreight(List<OrderSeparateFreightListRequest> requests) {
        //Step 1: 先判断收货地址Id是否全部大于0， 如果没有大于0的，运费为0
        List<Long> receiverAddressIds = requests.stream().flatMap(r -> r.getProducts().stream()).map(OrderSeparateProductFreightDetail::getReceiverAddressId).filter(id -> id.compareTo(0L) > 0).distinct().collect(Collectors.toList());
        if(CollectionUtils.isEmpty(receiverAddressIds)) {
            return Wrapper.success(requests.stream().map(r -> {
                OrderSeparateFreightListResponse response = new OrderSeparateFreightListResponse();
                response.setRequest(r.getRequest());
                response.setFreight(BigDecimal.ZERO);
                return response;
            }).collect(Collectors.toList()));
        }

        //Step 2: 批量查询收货地址
        List<ReceiverAddress> receiverAddresses = receiverAddressRepository.findAllById(receiverAddressIds);

        //Step 3: 直接映射返回结果
        List<OrderSeparateFreightListResponse> result = requests.stream().map(r -> {
            OrderSeparateFreightListResponse response = new OrderSeparateFreightListResponse();
            response.setRequest(r.getRequest());
            response.setFreight(BigDecimal.ZERO);

            if(CollectionUtils.isEmpty(r.getProducts())) {
                return response;
            }

            //按照收货地址分组
            Map<Long, List<OrderSeparateProductFreightDetail>> orderProductMap = r.getProducts().stream().collect(Collectors.groupingBy(OrderSeparateProductFreightDetail::getReceiverAddressId));

            //如果分组后只有一条数据，且数据的收货地址Id为0，运费为0
            if(CollectionUtils.isEmpty(orderProductMap) || (orderProductMap.size() == 1 && orderProductMap.containsKey(0L))) {
                return response;
            }

            //用reduce()方法，累计求运费和
           response.setFreight(orderProductMap.entrySet().stream().map(entry -> {
                //如果收货地址Id为0，运费为0
                if(entry.getKey().equals(0L)) {
                    return BigDecimal.ZERO;
                }

                //根据收货地址Id，找到收货地址，如果收货地址为Null，运费为0
                ReceiverAddress receiverAddress = receiverAddresses.stream().filter(address -> address.getId().equals(entry.getKey())).findFirst().orElse(null);
                if(Objects.isNull(receiverAddress)) {
                    return BigDecimal.ZERO;
                }

                //在分组的商品列表中过滤出运费模板id大于0，且数量大于0、重量大于0的商品列表，如果为空，运费为空，否则批量计算
                List<ProductFreightDetail> productFreightDetailList = entry.getValue().stream().filter(p -> p.getTemplateId().compareTo(0L) > 0 && p.getCount().compareTo(0D) > 0 && p.getWeight().compareTo(0D) > 0).map(e -> {
                    ProductFreightDetail productFreightDetail = new ProductFreightDetail();
                    productFreightDetail.setTemplateId(e.getTemplateId());
                    productFreightDetail.setCount(e.getCount());
                    productFreightDetail.setWeight(e.getWeight());
                    return productFreightDetail;
                }).collect(Collectors.toList());
                return CollectionUtils.isEmpty(productFreightDetailList) ? BigDecimal.ZERO : this.baseCalFreightPrice(productFreightDetailList, receiverAddress);
            }).reduce(BigDecimal.ZERO, BigDecimal::add));

            return response;
        }).collect(Collectors.toList());

        return Wrapper.success(result);
    }

//    @Override
//    public Wrapper<Map<OrderSeparateFreightRequest, BigDecimal>> findSeparateOrderFreight(Map<OrderSeparateFreightRequest, List<OrderSeparateProductFreightDetail>> products) {
//        Map<OrderSeparateFreightRequest, BigDecimal> resultMap = new HashMap<>();
//        for (Map.Entry<OrderSeparateFreightRequest, List<OrderSeparateProductFreightDetail>> entry : products.entrySet()) {
//            List<OrderSeparateProductFreightDetail> orderProduct = entry.getValue();
//            // 每笔订单运费赋值一个默认运费0
//            resultMap.put(entry.getKey(), BigDecimal.ZERO);
//            if (CollectionUtils.isEmpty(orderProduct)) {
//                continue;
//            }
//
//            // 按照收货地址分组
//            Map<Long, List<OrderSeparateProductFreightDetail>> orderProductMap = orderProduct.stream().collect(Collectors.groupingBy(OrderSeparateProductFreightDetail::getReceiverAddressId));
//            // 每笔订单的总运费
//            BigDecimal freightPrice = BigDecimal.ZERO;
//            for (Map.Entry<Long, List<OrderSeparateProductFreightDetail>> orderProductEntry : orderProductMap.entrySet()) {
//                // 收货地址
//                ReceiverAddress receiverAddress = receiverAddressRepository.findById(orderProductEntry.getKey()).orElse(null);
//                if (Objects.isNull(receiverAddress)) {
//                    continue;
//                }
//                List<ProductFreightDetail> productFreightDetailList = orderProductEntry.getValue().stream().map(e -> {
//                    ProductFreightDetail productFreightDetail = new ProductFreightDetail();
//                    productFreightDetail.setTemplateId(e.getTemplateId());
//                    productFreightDetail.setCount(e.getCount());
//                    productFreightDetail.setWeight(e.getWeight());
//                    return productFreightDetail;
//                }).collect(Collectors.toList());
//                BigDecimal bigDecimal = this.baseCalFreightPrice(productFreightDetailList, receiverAddress);
//                freightPrice = freightPrice.add(bigDecimal);
//            }
//
//            // 将默认运费修改为已计算的运费
//            resultMap.put(entry.getKey(), freightPrice);
//        }
//
//        return Wrapper.success(resultMap);
//    }

    private BigDecimal baseCalFreightPrice(List<ProductFreightDetail> orderProductList, ReceiverAddress receiverAddress) {
        // 运费模板
        List<Long> templateIdList = orderProductList.stream().map(ProductFreightDetail::getTemplateId).collect(Collectors.toList());
        List<FreightTemplate> templateList = freightTemplateRepository.findAllByIdInAndStatus(templateIdList, CommonStatusEnum.ENABLE.getCode());

        // 运费模板映射
        Map<Long, FreightTemplate> templateMap = templateList.stream().collect(Collectors.toMap(FreightTemplate::getId, e -> e, (e1, e2) -> e1));

        // 地区运费规则
        List<FreightTemplateDesignate> designateList = freightTemplateDesignateRepository.findAllByFreightTemplateIdIn(templateIdList);
        Map<Long, List<FreightTemplateDesignate>> designateMap = designateList.stream().collect(Collectors.groupingBy(FreightTemplateDesignate::getFreightTemplateId));

        // 运费模板id - 商品组
        Map<Long, List<ProductFreightDetail>> templateIdProductMap = orderProductList.stream().collect(Collectors.groupingBy(ProductFreightDetail::getTemplateId));

        // 总运费
        BigDecimal totalSumPrice = BigDecimal.ZERO;

        // 按重量计算运费, 相同运费模板商品累加计算
        for (Map.Entry<Long, List<ProductFreightDetail>> templateIdProductEntry : templateIdProductMap.entrySet()) {
            Long templateId = templateIdProductEntry.getKey();
            List<ProductFreightDetail> productList = templateIdProductEntry.getValue();
            // 运费模板
            FreightTemplate template = templateMap.get(templateId);
            if (Objects.isNull(template)) {
                continue;
            }

            // 运费规则
            List<FreightTemplateDesignate> templateDesignateList = Optional.ofNullable(designateMap.get(template.getId())).orElseGet(ArrayList::new);

            // 地区编码 - 指定地区信息
            Map<String, FreightTemplateDesignate> areaCodeDesignateMap = templateDesignateList.stream().flatMap(e -> e.getAreaIds().stream().map(i -> {
                Map<String, Object> map = new HashMap<>();
                map.put("areaId", i);
                map.put("designate", e);
                return map;
            })).collect(Collectors.toMap(map -> (String) map.get("areaId"), map -> (FreightTemplateDesignate) map.get("designate")));

            // 运费 = 首费 + 续费
            BigDecimal freightPrice = BigDecimal.ZERO;
            // 适用该模板的商品总重量
            BigDecimal totalWeight = BigDecimal.ZERO;
            for (ProductFreightDetail product : productList) {
                BigDecimal productTotalWeight = BigDecimal.valueOf(product.getWeight()).multiply(BigDecimal.valueOf(product.getCount()));
                totalWeight = totalWeight.add(productTotalWeight);
            }

            if (areaCodeDesignateMap.containsKey(receiverAddress.getProvinceCode())) {
                // 符合地区的运费规则
                FreightTemplateDesignate designate = areaCodeDesignateMap.get(receiverAddress.getProvinceCode());
                // 运费规则相关属性
                Double weight = designate.getWeight();
                BigDecimal price = designate.getPrice();
                Double incrementWeight = designate.getIncrementWeight();
                BigDecimal incrementPrice = designate.getIncrementPrice();

                freightPrice = calculateFreightPrice(totalWeight, weight, price, incrementWeight, incrementPrice);
            } else {
                // 默认运费规则
                // 运费规则相关属性
                Double weight = template.getWeight();
                BigDecimal price = template.getPrice();
                Double incrementWeight = template.getIncrementWeight();
                BigDecimal incrementPrice = template.getIncrementPrice();

                freightPrice = calculateFreightPrice(totalWeight, weight, price, incrementWeight, incrementPrice);
            }

            // 累加总运费
            totalSumPrice = totalSumPrice.add(freightPrice);
        }

        return totalSumPrice;
    }

    /**
     * 计算运费
     * @param totalWeight 商品总重量
     * @param weight 首重
     * @param price 首费
     * @param incrementWeight 续重
     * @param incrementPrice 续费
     * @return 返回结果
     */
    private BigDecimal calculateFreightPrice(BigDecimal totalWeight,
                                             Double weight,
                                             BigDecimal price,
                                             Double incrementWeight,
                                             BigDecimal incrementPrice
    ) {

        // 首费
        BigDecimal basePrice = BigDecimal.ZERO;
        // 续费
        BigDecimal extraPrice = BigDecimal.ZERO;

        // 多于首重, 增收续费
        if (totalWeight.compareTo(BigDecimal.valueOf(weight)) > 0) {
            // 首费
            basePrice = price;

            // 多于的重量
            BigDecimal sub = NumberUtil.sub(totalWeight, weight);
            // 实际算术重量
            BigDecimal realWeight = NumberUtil.div(sub, incrementWeight);
            // 向上取整
            BigDecimal round = NumberUtil.round(realWeight, 0, RoundingMode.UP);

            // 增收续费
            extraPrice = NumberUtil.mul(incrementPrice, round);
        } else {
            // 不多于首重, 则只收取首费
            basePrice = price;
        }

        return basePrice.add(extraPrice);
    }

    private void deleteDesignateByFreightTemplateId(Long id) {
        freightTemplateDesignateRepository.deleteByFreightTemplateId(id);
    }

    private void saveAllFreightTemplateDesignate(List<FreightTemplateRequest.FreightTemplateDesignateRequest> designateRequestList,
                                                 Long freightTemplateId) {
        if (!CollectionUtils.isEmpty(designateRequestList)) {
            List<FreightTemplateDesignate> saveList = designateRequestList.stream().map(e -> {
                FreightTemplateDesignate designate = new FreightTemplateDesignate();
                designate.setId(e.getId());
                designate.setFreightTemplateId(freightTemplateId);
                designate.setAreaIds(e.getAreaIds());
                designate.setWeight(e.getWeight());
                designate.setPrice(e.getPrice());
                designate.setIncrementWeight(e.getIncrementWeight());
                designate.setIncrementPrice(e.getIncrementPrice());
                return designate;
            }).collect(Collectors.toList());

            freightTemplateDesignateRepository.saveAll(saveList);
        }
    }

    private Wrapper<Void> checkFreightTemplateParam(FreightTemplateRequest request) {
        // 数据校验
        if (!CollectionUtils.isEmpty(request.getDesignateList())) {
            List<FreightTemplateRequest.FreightTemplateDesignateRequest> designateList = request.getDesignateList();
            for (FreightTemplateRequest.FreightTemplateDesignateRequest designateRequest : designateList) {
                Set<ConstraintViolation<FreightTemplateRequest.FreightTemplateDesignateRequest>> validateSet = validator.validate(designateRequest);
                for (ConstraintViolation<FreightTemplateRequest.FreightTemplateDesignateRequest> constraintViolation : validateSet) {
                    return Wrapper.fail(ResponseCode.REQUEST_PARAM_CHECK_FAILED, constraintViolation.getMessage());
                }
            }

            // 地区不能重复
            if (!designateList.stream().map(FreightTemplateRequest.FreightTemplateDesignateRequest::getAreaIds).flatMap(Collection::stream).allMatch(new HashSet<>()::add)) {
                return Wrapper.fail(ResponseCode.LS_AREA_NO_REPEAT);
            }
        }

        return Wrapper.success();
    }
}